package de.joergviola.mobj.client.database;

import java.util.Date;
import java.util.List;

import com.google.code.gwt.database.client.SQLError;
import com.google.code.gwt.database.client.SQLResultSet;
import com.google.code.gwt.database.client.SQLTransaction;
import com.google.code.gwt.database.client.StatementCallback;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONNumber;
import com.google.gwt.json.client.JSONString;

import de.joergviola.mobj.shared.Entity;
import de.joergviola.mobj.shared.EntityWriter;
import de.joergviola.mobj.shared.Persistable;
import de.joergviola.mobj.shared.Schema;
import de.joergviola.mobj.shared.UnknownEntityException;

public class DBEntityWriter implements EntityWriter {

	private Object[] args;
	private String[] fieldnames;
	private Schema schema;
	private SQLTransaction tx;

	public DBEntityWriter(Schema schema, SQLTransaction tx, String[] fieldnames) {
		this.schema = schema;
		this.tx = tx;
		this.fieldnames = fieldnames;
		args = new Object[fieldnames.length];
	}

	private int findIndex(String name) {
		for (int i = 0; i < fieldnames.length; i++) {
			if (name.equals(fieldnames[i])) {
				return i;
			}
		}
		return -1;
	}

	@Override
	public void writeString(String name, String value) {
		int index = findIndex(name);
		args[index] = value;
	}

	@Override
	public void writeLong(String name, Long value) {
		int index = findIndex(name);
		args[index] = value;
	}

	@Override
	public void writeDate(String name, Date value) {
		int index = findIndex(name);
		if (value == null)
			args[index] = null;
		else
			args[index] = Long.toString((value.getTime()));
	}

	@Override
	public void writeListLong(String name, List<Long> value) {
		int index = findIndex(name);
		if (value == null) {
			args[index] = null;
			return;
		}
		JSONArray array = new JSONArray();
		int i = 0;
		for (Long l : value) {
			array.set(i++, new JSONNumber(l));
		}
		args[index] = array.toString();
	}

	public Object[] getArgs() {
		return args;
	}

	@Override
	public void writeStringArray(String name, String[] value) {
		int index = findIndex(name);
		if (value == null) {
			args[index] = null;
			return;
		}

		JSONArray array = new JSONArray();
		for (int i = 0; i < value.length; i++) {
			array.set(i, new JSONString(value[i]));
		}
		args[index] = array.toString();
	}

	@Override
	public void writeDouble(String name, Double value) {
		int index = findIndex(name);
		args[index] = value;
	}

	@Override
	public void writeListString(String name, List<String> value) {
		int index = findIndex(name);
		if (value == null) {
			args[index] = null;
			return;
		}
		JSONArray array = new JSONArray();
		int i = 0;
		for (String s : value) {
			array.set(i++, new JSONString(s));
		}
		args[index] = array.toString();
	}

	@Override
	public void writePersistable(String name, Persistable value)
			throws UnknownEntityException {
		int index = findIndex(name);
		if (value == null)
			args[index] = null;
		else {
			args[index] = insertOrUpdate(value);
		}
	}

	private Object insertOrUpdate(final Persistable value)
			throws UnknownEntityException {
		final Entity<Persistable> entity = schema.getEntity(value);
		if (entity == null)
			throw new UnknownEntityException(value.getClass().getName());
		if (entity.getId(value) == null) {
			// Insert new Key!
			Insert<Persistable> insert = new Insert<Persistable>(schema,
					entity, value);
			insert.execute(tx);
		} else {
			// Try an update
			// If no row was updated, insert
			// The insert is done asynchrouneously!
			Update<Persistable> update = new Update<Persistable>(schema,
					entity, value);
			update.execute(tx, new StatementCallback<JavaScriptObject>() {

				@Override
				public void onSuccess(SQLTransaction transaction,
						SQLResultSet<JavaScriptObject> resultSet) {
					if (resultSet.getRowsAffected() == 0) {
						Insert<Persistable> insert = new Insert<Persistable>(
								schema, entity, value);
						try {
							insert.execute(tx);
						} catch (UnknownEntityException e) {
							// Cannot happen
						}
					}
				}

				@Override
				public boolean onFailure(SQLTransaction transaction,
						SQLError error) {
					return false;
				}
			});

		}
		return entity.getId(value);
	}

}
